﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace InnovTech.Util
{
    /// <summary>
    /// Represents a collection of WeakReference
    /// </summary>
    public class WeakRefCollection : IEnumerable<WeakReference>
    {
        const int DefaultAutoCleanupLimit = 20;

        private int _autoCleanupLimit;
        private List<WeakReference> _internalList;

        /// <summary>
        /// Creates a new instance of the type <see cref="WeakRefCollection"/>.
        /// </summary>
        /// <param name="autoCleanupLimit"></param>
        public WeakRefCollection(int autoCleanupLimit)
        {
            _autoCleanupLimit = autoCleanupLimit;
            _internalList = new List<WeakReference>();
        }

        /// <summary>
        /// Adds a <see cref="WeakReference"/>.
        /// </summary>
        /// <param name="wRef">A <see cref="WeakReference"/>.</param>
        public void Add(WeakReference wRef)
        {
            AutoCleanUp();
            if (!_internalList.Contains(wRef))
                _internalList.Add(wRef);
        }

        /// <summary>
        /// Adds a wrapper of the given generic type for the given object.
        /// </summary>
        /// <typeparam name="T">A sub-type of <see cref="WeakReference"/>.</typeparam>
        /// <param name="objToRef">An object.</param>
        public void AddObject<T>(object objToRef) where T : WeakReference
        {
            AutoCleanUp();
            if (!Contains(objToRef))
                Add(CreateReference<T>(objToRef));
        }

        /// <summary>
        /// Adds a wrapper <see cref="WeakReference"/> for the given object.
        /// </summary>
        /// <param name="objToRef">An object.</param>
        public void AddObject(object objToRef)
        {
            AddObject<WeakReference>(objToRef);
        }

        
       
        /// <summary>
        /// Removes a <see cref="WeakReference"/> from this collection.
        /// </summary>
        /// <param name="wr">a <see cref="WeakReference"/></param>
        public void Remove(WeakReference wr)
        {
            _internalList.Remove(wr);
        }

        /// <summary>
        /// Removes the wrapper WeakReference for the given object.
        /// </summary>
        /// <param name="obj">>An object.</param>
        public void RemoveObject(object obj)
        {
            WeakReference wr = GetRef(obj);
            if (wr != null)
                Remove(wr);
        }

        /// <summary>
        /// Checks whether this collecton contains a wapper for the given object.
        /// </summary>
        /// <param name="objToRef">>An object.</param>
        /// <returns>True, if contains.</returns>
        public bool Contains(object objToRef)
        {
            return GetRef(objToRef) != null;
        }

        /// <summary>
        /// Gets the wrapper WeakReference for the given object.
        /// </summary>
        /// <param name="refObj">An object which is contained by the current collection.</param>
        /// <returns>A WeakReference, if the current colleciton contains a wapper for the given object. Otherwise null.</returns>
        private WeakReference GetRef(object refObj)
        {
            return _internalList.SingleOrDefault(wr => wr.IsAlive && wr.Target == refObj);
        }

        /// <summary>
        /// Cleans up the Non-Alive WeakReference Instances from the internal list.
        /// </summary>
        public void CleanUp()
        {
            WeakReference[] refs = this._internalList.ToArray();
            foreach (WeakReference wr in refs)
            {
                if (!wr.IsAlive)
                    Remove(wr);
            }
        }

        /// <summary>
        /// Cleans up the Non-Alive WeakReference Instances from the internal list automatically depending on the pre-defined auto-clean-up-threshold.
        /// </summary>
        private void AutoCleanUp()
        {
            if (_internalList.Count >= _autoCleanupLimit)
                CleanUp();
        }
        
        /// <summary>
        /// Creates a special WeakReference instance depending on the given sub-type of WeakReference.
        /// </summary>
        /// <typeparam name="T">A sub-type of WeakReference</typeparam>
        /// <param name="obj">The target of the new WeakReference instance.</param>
        /// <returns>A new instance of the given sub-type.</returns>
        private T CreateReference<T>(object obj) where T : WeakReference
        {
            return Activator.CreateInstance(typeof(T), obj) as T;
        }


        #region IEnumberable Members
        public IEnumerator<WeakReference> GetEnumerator()
        {
            return _internalList.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        #endregion IEnumberable Members
    }
}
